package edu.syr.iis.web.model;


import java.util.ArrayList;
import java.util.Set;
import java.util.List;
import java.util.HashSet;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Message
{
	private static Log log = LogFactory.getLog(Message.class);
	
	// For speaker
	public static final int Librarian = 1;
	public static final int User = 2;

	// For type
	public static final int Message = 1;
	public static final int Automatic = 2;
	public static final int Special = 3;

	int messageId;    

	int sequence;
	int speaker;
	int type;

	String dateTime;
	String content;
	String note;

	Conversation conversation;
	
	Set<DialogueActLabel> dialogueActLabels = new HashSet<DialogueActLabel>();	
	Set<DialogueAct> dialogueActs = new HashSet<DialogueAct>();
	
	DialogueAct[] dialogueActArray = null;
	SortedSet<DialogueAct> sortedDialogueActs = null;

	public String getAgreementString()
	{
		Set<User> annotators = conversation.getAnnotators();
		String agreement = String.format("Overall: %.2f, ", getAgreement());
		
		for (User annotator: annotators)
			agreement += String.format("%s: %.2f, ", annotator.getUserName(), getAgreement(annotator));
			
		agreement = agreement.replaceFirst(",\\s$", "");
		return agreement;
	}
	
	public double getFunctionAgreement(User user)
	{
		Set <DialogueAct> dialogueActs = getDialogueActs(user);
		int count = 0;
		
		for (DialogueAct dialogueAct: dialogueActs)
		{
			// Assuming the non-XOR version of getMatchedDialogueActs()
			//
			if (dialogueAct.getMatchedDialogueActs(DialogueAct.MATCH_FUNCTION).size() > 0)
				count++;
		}
		// If the annotator hasn't annotated, it will return NaN
		//
		return ((double) count) / dialogueActs.size();
	}
	
	
	public double getMessageWiseAgreement(User user)
	{
		Set <DialogueAct> dialogueActs = getDialogueActs(user);
		int count = 0;
		
		for (DialogueAct dialogueAct: dialogueActs)
		{
			/*
			 * THis is an old version
			 
			// these methods are mutually, exclusive so you need to call both of them 
			//
			if (dialogueAct.getMatchedDialogueActs().size() +			
				dialogueAct.getExactlyMatchedDialogueActs().size() > 0)
				count++;
			*/
			if (dialogueAct.getMatchedDialogueActs(DialogueAct.MATCH_DA).size() > 0)
				count++;				
		}
		// If the annotator hasn't annotated, it will return NaN
		//
		return ((double) count) / dialogueActs.size();
	}
		
	
	public double getAgreement(User user)
	{
		Set <DialogueAct> dialogueActs = getDialogueActs(user);
		int count = 0;
		
		for (DialogueAct dialogueAct: dialogueActs)
		{
			if (dialogueAct.getExactlyMatchedDialogueActs().size() > 0)
				count++;
		}
		// If the annotator hasn't annotated, it will return NaN
		//
		return ((double) count) / dialogueActs.size();
	}
	
	public double getAgreement()
	{
		Set<User> annotators = conversation.getAnnotators();
		double sum = 0.0;
		int count = 0;  // not using annotators.size(), because there are annotators that haven't done the job. 
		
		for (User annotator: annotators)
		{
			if (getDialogueActs(annotator).size() > 0)
			{
				sum += getAgreement(annotator);
				count++;
			}
		}
			
		return sum / count;
	}
	
	
	public Set<DialogueAct> getDialogueActs(User user)
	{
		Set<DialogueAct> das = new HashSet<DialogueAct>();
		
		for (DialogueAct da: dialogueActs)
			if (da.getUser().getUserId() == user.getUserId())
				das.add(da);
		return das;		
	}
	
	public DialogueAct getNextDialogueAct (DialogueAct da)
	{
		Object [] daList = sortedDialogueActs.toArray();
		
		for (int i = 0; i < daList.length; i++) {
			if (daList[i] == da)
				if (i != daList.length - 1) {
					DialogueAct candidate =  (DialogueAct) daList[i + 1];
					//
					// If the next dialogue act is in the same text segment, ignore and move to the next one
					//
					if (candidate.getStartIndex() == da.getStartIndex())
						getNextDialogueAct(candidate);   
					else 
						return candidate;
				}
				// else	
				//	return null;				
		}
		if (sequence == conversation.numMessages)
			return null;
		
		Message nextMessage = conversation.getMessageBySequence(sequence + 1);
		if (nextMessage != null) 
			return nextMessage.getLastDialogueAct();
		return null;		
	}
	
	public DialogueAct getPreviousDialogueAct (DialogueAct da)
	{
		Object [] daList = /*(DialogueAct [])*/ this.getSortedDialogueActs().toArray();
		
		for (int i = 0; i < daList.length; i++) {			
			if (daList[i] == da) 
				if (i != 0) {
					DialogueAct candidate =  (DialogueAct) daList[i - 1];
					//
					// If the next dialogue act is in the same text segment, ignore and move to the next one
					//
					if (candidate.getStartIndex() == da.getStartIndex())
						getNextDialogueAct(candidate);   
					else 
						return candidate;			
				}
		}
		
		if (sequence == 1)
			return null;
		
		Message prevMessage = conversation.getMessageBySequence(sequence - 1);
		if (prevMessage != null) 
			return prevMessage.getLastDialogueAct();
		return null;		
	}
	
	public DialogueAct getLastDialogueAct()
	{
		SortedSet<DialogueAct> das = getSortedDialogueActs();
		if (!das.isEmpty())
			return das.last();
		else
			return null;		
	}
	
	public DialogueAct getFirstDialogueAct()
	{
		SortedSet<DialogueAct> das = getSortedDialogueActs();
		if (!das.isEmpty())
			return das.first();
		else
			return null;
	}
	
    public DialogueAct[] getDialogueActArray()
    {
    	if (dialogueActArray == null || dialogueActArray.length != dialogueActs.size()) {     		
        	dialogueActArray = dialogueActs.toArray(new DialogueAct[0]);
    	}
    	
    	return dialogueActArray; 
    }        

	public SortedSet<DialogueAct> getSortedDialogueActs()
    {    	
    	log.debug("Entering getSortedDialogueActs() with dialogueActs of size: " + dialogueActs.size());
    	
    	// if (sortedDialogueActs == null || sortedDialogueActs.size() != dialogueActs.size()) 
    	// {    		
    		SortedSet<DialogueAct> sortedDas = new TreeSet<DialogueAct>(new DialogueActComparator());
    		for (DialogueAct dialogueAct: dialogueActs)
    			sortedDas.add(dialogueAct);
    		sortedDialogueActs = sortedDas;
    	// }
    	log.debug("Existing getSortedDialogueActs() with sortedDialogueActs of size: " + sortedDialogueActs.size());
    	return sortedDialogueActs;
    }
        
	
	
	public String getNote()
	{
		if (note == null || note.matches("^ *$")) {
			return "";
		}

		return note;
	}

	public void setNote(String note)
	{
		if (note != null)
			this.note = note.trim();
		else
			this.note = null;
	}


	public String getContentStringEscaped()
	{
		return content.trim().replaceAll("\"", "&quot;");
	}
	
	public String getContentStringPlain()
	{
		//System.out.println("Content: " + content + ";");

		if (content.startsWith("http:"))
		{
			return (content.length() > 60)? content.substring(0, 59) + "..." : content;
		} 
		else if (content.matches("^\\[.*\\]\\s*$"))
		{
			return new String(content.replace('_', ' ').trim());
		}
		return content.trim();
	}

	public String getContentString()
	{
		String shortContent;

		//System.out.println("Content: " + content + ";");

		if (content.startsWith("http:"))
		{
			shortContent = (content.length() > 60)? content.substring(0, 59):content;
			return new String("<a href=\"" + content + "\">" + shortContent  + "..." + "</a>");	    
		} 
		else if (content.matches("^\\[.*\\]\\s*$"))
		{
			return new String(content.replace('_', ' ').trim());
		}
		return content.trim();
	}

	
	public List<WordListItem> getWordList()
	{
		
		String contentCopy = new String(content);
		
		if (contentCopy.matches("^\\[.*\\]\\s*$"))
			contentCopy = contentCopy.replace('_', ' ');
		String[] words = contentCopy.split("(\\s+)");
		List<WordListItem> wordList = new ArrayList<WordListItem>();
		
		int index = 0;
		
		for (String word: words)
		{
			wordList.add(index, new WordListItem(word, index));
			index++;
		}
		return wordList;
	}

	public class WordListItem
	{
		String word;
		int index;
				
		public WordListItem(String word, int index) {
			super();
			this.word = word;
			this.index = index;
		}
		
		public String getWord() {
			return word;
		}
		public void setWord(String word) {
			this.word = word;
		}
		public int getIndex() {
			return index;
		}
		public void setIndex(int index) {
			this.index = index;
		}			
	}
	
	public Conversation getConversation()
	{
		return conversation;
	}
	public void setConversation(Conversation conversation)
	{
		this.conversation = conversation;
	}
	public int getMessageId()
	{
		return messageId;
	}
	public int getSequence()
	{
		return sequence;
	}
	public int getSpeaker()
	{
		return speaker;
	}

	public String getTime()
	{	
		return dateTime.substring(dateTime.indexOf(" "));
	}

	public String getDateTime()
	{
		return dateTime;
	}
	public String getContent()
	{
		return content;
	}
	public int getType()
	{
		return type;
	}
	public void setMessageId(int messageId)
	{
		this.messageId = messageId;
	}
	public void setSequence(int sequence)
	{
		this.sequence = sequence;
	}
	public void setSpeaker(int speaker)
	{
		this.speaker = speaker;
	}
	public void setDateTime(String dateTime)
	{
		this.dateTime = dateTime;
	}
	public void setContent(String content)
	{
		this.content = content;
	}
	public void setType(int type)
	{
		this.type = type;
	}

	public Set<DialogueActLabel> getDialogueActLabels()
	{
		return dialogueActLabels;
	}

	public Set<DialogueAct> getDialogueActs()
	{		
		return dialogueActs;
	}

	public void setDialogueActLabels(Set<DialogueActLabel> dialogueActLabels)
	{
		this.dialogueActLabels = dialogueActLabels;
	}


	public void setDialogueActs(Set<DialogueAct> dialogueActs)
	{
		this.dialogueActs = dialogueActs;
	}

	// Does not work, because the new DA is not defined.
	//
	public void addDialogueAct()
	{    	
		this.dialogueActs.add(new DialogueAct());
	}

	public void addDialogueAct(DialogueAct da)
	{	
		dialogueActs.add(da);
	}

	public void deleteDialogueAct(int daId)
	{	
		HashSet<DialogueAct> newDAs = new HashSet<DialogueAct>();
		System.out.println("Size of dialogueActs: " + dialogueActs.size());
		System.out.println("Removing from Set: " + daId);
		
		for (DialogueAct curDA: this.dialogueActs)
		{
			if (curDA.getDialogueActId() == daId)
				System.out.println("Found the DA ID: " + curDA.getDialogueActId());
			else
				newDAs.add(curDA);
		}	
		this.dialogueActs = newDAs;
		System.out.println("Size of dialogueActs: " + dialogueActs.size());
	}

}
